package cn.iocoder.yudao.module.iot.gateway.protocol.emqx.router;

import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.module.iot.core.mq.message.IotDeviceMessage;
import cn.iocoder.yudao.module.iot.gateway.protocol.emqx.IotEmqxUpstreamProtocol;
import cn.iocoder.yudao.module.iot.gateway.service.device.message.IotDeviceMessageService;
import cn.iocoder.yudao.module.iot.gateway.service.device.IotDeviceService;
import cn.iocoder.yudao.module.iot.gateway.service.scene.IotSceneService;
import cn.iocoder.yudao.module.iot.core.biz.dto.IotSceneRespDTO;
import cn.iocoder.yudao.module.iot.core.biz.dto.IotDeviceRespDTO;
import cn.iocoder.yudao.module.iot.core.util.IotDeviceMessageUtils;
import io.vertx.mqtt.messages.MqttPublishMessage;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.beans.factory.annotation.Qualifier;

/**
 * IoT 网关 EMQX 上行消息处理器
 *
 * @author 芋道源码
 */
@Slf4j
public class IotEmqxUpstreamHandler {

    private final IotDeviceService deviceService;
    private final IotSceneService sceneService;
    private final IotDeviceMessageService deviceMessageService;
    private final IotEmqxUpstreamProtocol protocol;
    private final String serverId;
    private final RedisTemplate<String, Object> redisTemplate;

    public IotEmqxUpstreamHandler(IotEmqxUpstreamProtocol protocol) {
        this.deviceService = SpringUtil.getBean(IotDeviceService.class);
        this.sceneService = SpringUtil.getBean(IotSceneService.class);
        this.deviceMessageService = SpringUtil.getBean(IotDeviceMessageService.class);
        this.protocol = protocol;
        this.serverId = protocol.getServerId();
        this.redisTemplate = SpringUtil.getBean("redisTemplate", RedisTemplate.class);
    }

    /**
     * 处理 MQTT 发布消息
     */
    public void handle(MqttPublishMessage mqttMessage) {
        log.info("[handle][收到 MQTT 消息, topic: {}, payload: {}]", mqttMessage.topicName(), mqttMessage.payload());
        String topic = mqttMessage.topicName();
        byte[] payload = mqttMessage.payload().getBytes();
        try {
            // 1. 解析主题，一次性获取所有信息
            String[] topicParts = topic.split("/");
            if (topicParts.length < 4 || StrUtil.hasBlank(topicParts[2], topicParts[3])) {
                log.warn("[handle][topic({}) 格式不正确，无法解析有效的 productKey 和 deviceName]", topic);
                return;
            }

            String productKey = topicParts[2];
            String deviceName = topicParts[3];

            // 2. 解码消息
            IotDeviceMessage message = deviceMessageService.decodeDeviceMessage(payload, productKey, deviceName);
            if (message == null) {
                log.warn("[handle][topic({}) payload({}) 消息解码失败]", topic, new String(payload));
                return;
            }

            // 3. 检查是否为上行消息，如果是则进行场景条件检查
            if (IotDeviceMessageUtils.isUpstreamMessage(message) && message.getCode() == null) {
                log.info("[handle][处理设备上行消息，设备ID: {}, 方法: {}, 参数: {}]", 
                    message.getDeviceId(), message.getMethod(), message.getParams());
                
                // 3.1 获取设备信息
                IotDeviceRespDTO deviceInfo = deviceService.getDeviceFromCache(message.getDeviceId());
                if (deviceInfo == null) {
                    log.error("[handle][设备信息({})不存在]", message.getDeviceId());
                    return;
                }

                // 3.2 检查params字段，只处理有数据的上行消息
                Object paramsObj = message.getParams();
                if (paramsObj == null || !(paramsObj instanceof java.util.Map)) {
                    log.debug("[handle][跳过处理，params字段为空或不是Map类型，deviceId: {}, method: {}, params: {}]", 
                        message.getDeviceId(), message.getMethod(), paramsObj);
                    return;
                }

                // 3.3 获取当前北京时间
                ZonedDateTime beijingTime = ZonedDateTime.now(ZoneId.of("Asia/Shanghai"));
                LocalTime currentTime = beijingTime.toLocalTime();
                String currentTimeStr = currentTime.format(DateTimeFormatter.ofPattern("HH:mm"));
                log.info("[handle][当前北京时间: {}]", currentTimeStr);
                
                // 3.4 获取所有启用的场景（包括定时场景）
                List<IotSceneRespDTO> allScenes = sceneService.getAllEnabledScenes();
                log.info("[handle][设备ID:{} 查询到{}个启用场景]", message.getDeviceId(), allScenes.size());
                
                for (IotSceneRespDTO scene : allScenes) {
                    if (scene.getConditions() == null) continue;
                    boolean match = false;
                    log.info("[handle][检查场景ID:{} 的条件匹配]", scene.getId());
                    
                    for (var cond : scene.getConditions()) {
                        // 检查设备条件
                        if (cond.getDeviceId() != null && cond.getDeviceId().equals(message.getDeviceId())) {
                            @SuppressWarnings("unchecked")
                            java.util.Map<String, Object> params = (java.util.Map<String, Object>) paramsObj;
                            Object val = params.get(cond.getAttr());
                            
                            log.info("[handle][设备条件检查 - 属性:{} 期望值:{} 实际值:{} 操作符:{}]", 
                                cond.getAttr(), cond.getValue(), val, cond.getOperator());
                            
                            if (val != null && compare(val.toString(), cond.getValue(), cond.getOperator())) {
                                match = true;
                                log.info("[handle][设备条件匹配成功 - 场景ID:{} 属性:{} 期望值:{} 实际值:{}]", 
                                    scene.getId(), cond.getAttr(), cond.getValue(), val);
                            }
                        }
                        
                        // 检查定时条件
                        if ("定时".equals(cond.getAttr()) || "timer".equals(cond.getAttr())) {
                            log.info("[handle][定时条件检查 - 期望时间:{} 当前时间:{}]", cond.getValue(), currentTimeStr);
                            
                            // 检查时间是否匹配
                            boolean timeMatch = currentTimeStr.equals(cond.getValue());
                            
                            // 检查周几条件
                            boolean dayMatch = checkWeekDayCondition(cond);
                            
                            if (timeMatch && dayMatch) {
                                match = true;
                                log.info("[handle][定时条件匹配成功 - 场景ID:{} 期望时间:{} 当前时间:{}]", 
                                    scene.getId(), cond.getValue(), currentTimeStr);
                            } else {
                                log.debug("[handle][定时条件不匹配 - 场景ID:{} 时间匹配:{} 周几匹配:{}]", 
                                    scene.getId(), timeMatch, dayMatch);
                            }
                        }
                        
                        // 检查场景条件
                        if ("SCENE".equals(cond.getAttr()) || "scene".equals(cond.getAttr()) || "场景".equals(cond.getAttr())) {
                            log.info("[handle][场景条件检查 - 条件值:{}]", cond.getConditionValue());
                            try {
                                java.util.Map<String, Object> sceneInfo = null;
                                if (cond.getConditionValue() != null) {
                                    try {
                                        String jsonStr = cond.getConditionValue();
                                        if (jsonStr.startsWith("{") && jsonStr.endsWith("}")) {
                                            @SuppressWarnings("unchecked")
                                            java.util.Map<String, Object> parsed = cn.hutool.json.JSONUtil.toBean(jsonStr, java.util.Map.class);
                                            sceneInfo = parsed;
                                        }
                                    } catch (Exception e) {
                                        log.warn("[handle][JSON解析失败 - 条件值:{}]", cond.getConditionValue(), e);
                                    }
                                }
                                
                                if (sceneInfo != null) {
                                    Object sceneIdObj = sceneInfo.get("sceneId");
                                    Long targetSceneId = null;
                                    if (sceneIdObj instanceof Integer) {
                                        targetSceneId = ((Integer) sceneIdObj).longValue();
                                    } else if (sceneIdObj instanceof Long) {
                                        targetSceneId = (Long) sceneIdObj;
                                    } else if (sceneIdObj instanceof String) {
                                        targetSceneId = Long.valueOf((String) sceneIdObj);
                                    }
                                    
                                    String sceneExecution = (String) sceneInfo.get("sceneExecution");
                                    
                                    log.info("[handle][场景条件解析 - 目标场景ID:{} 执行状态:{}]", targetSceneId, sceneExecution);
                                    
                                    if (targetSceneId != null) {
                                        // 检查目标场景是否存在
                                        IotSceneRespDTO targetScene = sceneService.getSceneFromCache(targetSceneId);
                                        if (targetScene != null) {
                                            log.info("[handle][目标场景存在 - 场景ID:{} 场景名称:{}]", 
                                                targetScene.getId(), targetScene.getSceneName());
                                            
                                            // 检查目标场景是否正在执行
                                            boolean isExecuting = checkSceneExecutingStatus(targetSceneId);
                                            
                                            if ("EXECUTE".equals(sceneExecution) && isExecuting) {
                                                match = true;
                                                log.info("[handle][场景条件匹配成功 - 场景ID:{} 目标场景ID:{} 正在执行]", 
                                                    scene.getId(), targetSceneId);
                                            } else if ("STOP".equals(sceneExecution) && !isExecuting) {
                                                match = true;
                                                log.info("[handle][场景条件匹配成功 - 场景ID:{} 目标场景ID:{} 已停止]", 
                                                    scene.getId(), targetSceneId);
                                            } else {
                                                log.debug("[handle][场景条件不匹配 - 场景ID:{} 目标场景ID:{} 期望执行状态:{} 实际执行状态:{}]", 
                                                    scene.getId(), targetSceneId, sceneExecution, isExecuting);
                                            }
                                        } else {
                                            log.warn("[handle][目标场景不存在 - 场景ID:{}]", targetSceneId);
                                        }
                                    } else {
                                        log.warn("[handle][场景ID解析失败 - 原始值:{}]", sceneIdObj);
                                    }
                                } else {
                                    log.warn("[handle][场景条件值解析失败 - 条件值:{}]", cond.getConditionValue());
                                }
                            } catch (Exception e) {
                                log.error("[handle][场景条件处理异常 - 场景ID:{} 条件值:{}]", scene.getId(), cond.getConditionValue(), e);
                            }
                        }
                    }
                    
                    if (match && scene.getActions() != null) {
                        log.info("[handle][场景ID:{} 条件匹配成功，开始执行{}个动作]", scene.getId(), scene.getActions().size());
                        
                        // 设置场景为执行状态
                        setSceneExecutingStatus(scene.getId(), true);
                        
                        try {
                            for (var action : scene.getActions()) {
                                log.info("[handle][执行动作 - 场景ID:{} 设备ID:{} 动作类型:{} 动作值:{}]", 
                                    scene.getId(), action.getDeviceId(), action.getActionType(), action.getActionValue());
                                sendActionToDevice(action);
                            }
                            
                            // 延迟清除执行状态，给其他场景检查留出时间窗口
                            // 使用异步任务延迟清除，避免阻塞当前线程
                            new Thread(() -> {
                                try {
                                    Thread.sleep(5000); // 延迟5秒清除
                                    setSceneExecutingStatus(scene.getId(), false);
                                } catch (InterruptedException e) {
                                    Thread.currentThread().interrupt();
                                }
                            }).start();
                            
                        } catch (Exception e) {
                            log.error("[handle][场景ID:{} 执行动作失败]", scene.getId(), e);
                            // 执行失败时也要清除执行状态
                            setSceneExecutingStatus(scene.getId(), false);
                        }
                    } else if (match) {
                        log.warn("[handle][场景ID:{} 条件匹配成功，但没有找到动作]", scene.getId());
                    } else {
                        log.debug("[handle][场景ID:{} 条件不匹配]", scene.getId());
                    }
                }
            }

            // 4. 发送消息到队列（转发给主服务）
            deviceMessageService.sendDeviceMessage(message, productKey, deviceName, serverId);
        } catch (Exception e) {
            log.error("[handle][topic({}) payload({}) 处理异常]", topic, new String(payload), e);
        }
    }

    /**
     * 比较值
     */
    private boolean compare(String actual, String expected, String operator) {
        if (actual == null || expected == null) {
            return false;
        }
        
        try {
            double actualVal = Double.parseDouble(actual);
            double expectedVal = Double.parseDouble(expected);
            
            return switch (operator) {
                case "=", "==" -> Math.abs(actualVal - expectedVal) < 0.001;
                case "!=", "<>" -> Math.abs(actualVal - expectedVal) >= 0.001;
                case ">" -> actualVal > expectedVal;
                case ">=" -> actualVal >= expectedVal;
                case "<" -> actualVal < expectedVal;
                case "<=" -> actualVal <= expectedVal;
                default -> actual.equals(expected);
            };
        } catch (NumberFormatException e) {
            // 如果不是数字，按字符串比较
            return switch (operator) {
                case "=", "==" -> actual.equals(expected);
                case "!=", "<>" -> !actual.equals(expected);
                case ">" -> actual.compareTo(expected) > 0;
                case ">=" -> actual.compareTo(expected) >= 0;
                case "<" -> actual.compareTo(expected) < 0;
                case "<=" -> actual.compareTo(expected) <= 0;
                default -> actual.equals(expected);
            };
        }
    }

    /**
     * 检查周几条件
     */
    private boolean checkWeekDayCondition(Object condition) {
        // TODO: 实现周几条件检查逻辑
        return true;
    }

    /**
     * 检查场景是否正在执行
     */
    private boolean checkSceneExecutingStatus(Long sceneId) {
        try {
            // 使用Redis缓存检查场景的执行状态
            // 场景执行时会在Redis中设置一个临时标记，执行完成后清除
            String redisKey = String.format("iot:scene:executing:%d", sceneId);
            Boolean isExecuting = redisTemplate.hasKey(redisKey);
            
            if (isExecuting != null && isExecuting) {
                log.debug("[checkSceneExecutingStatus][检查场景执行状态 - 场景ID:{} 当前状态: 执行中]", sceneId);
                return true;
            } else {
                log.debug("[checkSceneExecutingStatus][检查场景执行状态 - 场景ID:{} 当前状态: 未执行]", sceneId);
                return false;
            }
        } catch (Exception e) {
            log.warn("[checkSceneExecutingStatus][检查场景执行状态失败 - 场景ID:{}]", sceneId, e);
            return false;
        }
    }

    /**
     * 设置场景执行状态
     */
    private void setSceneExecutingStatus(Long sceneId, boolean isExecuting) {
        try {
            String redisKey = String.format("iot:scene:executing:%d", sceneId);
            if (isExecuting) {
                // 设置场景为执行状态，30秒后自动过期
                redisTemplate.opsForValue().set(redisKey, "1", java.time.Duration.ofSeconds(30));
                log.debug("[setSceneExecutingStatus][设置场景执行状态 - 场景ID:{} 状态:执行中]", sceneId);
            } else {
                // 清除场景执行状态
                redisTemplate.delete(redisKey);
                log.debug("[setSceneExecutingStatus][清除场景执行状态 - 场景ID:{} 状态:未执行]", sceneId);
            }
        } catch (Exception e) {
            log.warn("[setSceneExecutingStatus][设置场景执行状态失败 - 场景ID:{} 状态:{}]", sceneId, isExecuting, e);
        }
    }

    /**
     * 发送动作到设备
     */
    private void sendActionToDevice(Object action) {
        try {
            if (action instanceof cn.iocoder.yudao.module.iot.core.biz.dto.IotSceneActionDTO) {
                cn.iocoder.yudao.module.iot.core.biz.dto.IotSceneActionDTO actionDTO = 
                    (cn.iocoder.yudao.module.iot.core.biz.dto.IotSceneActionDTO) action;
                
                log.info("[sendActionToDevice][发送动作到设备 - 设备ID:{} 动作类型:{} 动作值:{}]", 
                    actionDTO.getDeviceId(), actionDTO.getActionType(), actionDTO.getActionValue());
                
                // 1. 获取设备信息
                IotDeviceRespDTO deviceInfo = deviceService.getDeviceFromCache(actionDTO.getDeviceId());
                if (deviceInfo == null) {
                    log.error("[sendActionToDevice][设备信息({})不存在]", actionDTO.getDeviceId());
                    return;
                }
                
                // 2. 构建设备控制消息
                IotDeviceMessage controlMessage = new IotDeviceMessage();
                controlMessage.setMethod("thing.property.set");
                controlMessage.setDeviceId(actionDTO.getDeviceId());
                
                // 3. 构建参数
                java.util.Map<String, Object> params = new java.util.HashMap<>();
                params.put(actionDTO.getActionType(), actionDTO.getActionValue());
                controlMessage.setParams(params);
                
                // 4. 编码消息
                byte[] payload = deviceMessageService.encodeDeviceMessage(controlMessage, 
                    deviceInfo.getProductKey(), deviceInfo.getDeviceName());
                
                // 5. 构建MQTT主题
                String topic = String.format("/iot/%s/%s/thing/property/set",
                    deviceInfo.getProductKey(), deviceInfo.getDeviceName());
                
                // 6. 发布消息到MQTT
                protocol.publishMessage(topic, payload);
                log.info("[sendActionToDevice][MQTT消息发送成功 - 主题:{}]", topic);
                
            } else {
                log.warn("[sendActionToDevice][未知的动作类型:{}]", action.getClass().getName());
            }
        } catch (Exception e) {
            log.error("[sendActionToDevice][发送动作到设备失败 - 动作:{}]", action, e);
        }
    }
}